/*************************************************************************

Framework\System.Web.Services.Protocols.HttpPostClientProtocol.cpp

	-by Miles Chen (stainboyx@hotmail.com) 2008-12-27

*************************************************************************/

#pragma once

#include "commdef-int.h"

N System{N Web{N Services{N Protocols{N HttpPostClientProtocol{




#define SOCKET_DEFAULT_BUFFER_LENGTH 2048


	int AppendString(char* p, int index, const char* q)
	{
		p += index;

		while(*q)
		{
			*p++ = *q++;
			index++;
		}

		return index;
	}

	int AppendSeparator(char* p, int index)
	{
		p[index++] = 0x0D;
		p[index++] = 0x0A;

		return index;
	}

	int GetXmlFromResponse(const char* lpBuffer, int iLen, wchar_t* lpszXml)
	{
		// <?xml version="1.0" encoding="utf-8"?>
		char* p = (char*)lpBuffer;
		while(*p && *p != '<')
			p++;

		return MultiByteToWideChar(CP_UTF8, 0, p, -1, lpszXml, iLen);
	}

	BOOL InitializeWSA()
	{
		int ir;
		WSADATA wsa;

		ir = WSAStartup(MAKEWORD(2,2), &wsa);
		if (ir != NO_ERROR) 
		{
			//Debug::AssertEx("WSAStartup failed with error: %d\n", ir);
			return FALSE;
		}

		return TRUE;
	}

	BOOL ConnectToServer(LPCSTR lpszRemoteURL, SOCKET* s)
	{
		BOOL fr = TRUE;
		int ir;
		addrinfo* pai = NULL;
		addrinfo hint = {0};
		char lpszAddr[SHORT_CHAR], lpszPort[SHORT_CHAR];
		int iFirstSlash;
		int iFirstColon;
		int iNextSlash;


		//---------------------------------------------------
		*s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (*s == INVALID_SOCKET) 
		{
			//Debug::AssertEx("socket failed with error: %ld\n", WSAGetLastError());
			fr = FALSE;
			goto __exit;
		}


		//---------------------------------------------------
		iFirstSlash = String::IndexOf(lpszRemoteURL, "://", 0);
		iFirstColon = String::IndexOf(lpszRemoteURL, ":", iFirstSlash + 3);
		iNextSlash = String::IndexOf(lpszRemoteURL, "/", iFirstSlash + 3);

		if(iFirstColon != -1 && iFirstColon < iNextSlash)
		{
			// there is a specific port
			char *p = lpszPort, 
				*q = (char*)lpszRemoteURL + iFirstColon + 1;
			for(int i=0; i<iNextSlash - (iFirstColon + 1); i++)
				*p++ = *q++;
			*p = '\0';

			p = lpszAddr;
			q = (char*)lpszRemoteURL + iFirstSlash + 3;
			for(int i=0; i<iFirstColon - (iFirstSlash + 3); i++)
				*p++ = *q++;
			*p = '\0';
		}
		else
		{
			// use default port 80
			String::CopyTo(lpszPort, "80");

			char *p = lpszAddr, 
				*q = (char*)lpszRemoteURL + iFirstSlash + 3;
			for(int i=0; i<iNextSlash - (iFirstSlash + 3); i++)
				*p++ = *q++;
			*p = '\0';
		}

		//Console::WriteLineEx("Addr: %s, Port: %s", lpszAddr, lpszPort);


		//---------------------------------------------------
		hint.ai_family = AF_INET;
		hint.ai_socktype = SOCK_STREAM;
		hint.ai_protocol = IPPROTO_TCP;

		ir = getaddrinfo(lpszAddr, lpszPort, &hint, &pai);
		if(ir != 0)
		{
			//Debug::AssertEx("GetAddrInfoW failed, %d", ir);
			fr = FALSE;
			goto __exit;
		}


		//---------------------------------------------------
		ir = connect(*s, pai->ai_addr, sizeof(struct sockaddr));
		if (ir == SOCKET_ERROR) 
		{
			//Debug::AssertEx( "connect failed with error: %d\n", WSAGetLastError() );
			fr = FALSE;
			goto __exit;
		}

__exit:
		if(pai)
			freeaddrinfo(pai);
		if(!fr && *s != INVALID_SOCKET)
			closesocket(*s);

		return fr;
	}

	BOOL SendRequest(LPCSTR lpszRemoteURL, LPCSTR lpszMethod, LPCSTR lpszPostData, SOCKET s)
	{
		int nIndex;
		BOOL fr = TRUE;
		int ir;
		int iFirstSlash;
		int iNextSlash;
		char tbuf[MAX_CHAR];
		char sendbuf[SOCKET_DEFAULT_BUFFER_LENGTH] = {0};


		//---------------------------------------------------
		nIndex = 0;

		iFirstSlash = String::IndexOf(lpszRemoteURL, "://", 0);
		iNextSlash = String::IndexOf(lpszRemoteURL, "/", iFirstSlash + 3);
		wsprintfA(tbuf, "POST %s/%s HTTP/1.1", lpszRemoteURL + iNextSlash, lpszMethod);

		//nIndex = AppendString(sendbuf, nIndex, "POST /x509.asmx/TryValidateClient HTTP/1.1");
		nIndex = AppendString(sendbuf, nIndex, tbuf);
		nIndex = AppendSeparator(sendbuf, nIndex);

		String::SubString(lpszRemoteURL, tbuf, (iFirstSlash + 3), iNextSlash - (iFirstSlash + 3));

		nIndex = AppendString(sendbuf, nIndex, "Host: ");
		nIndex = AppendString(sendbuf, nIndex, tbuf);
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "Accept: */*");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "Referer: (*)");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "Accept-Language: *");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "User-Agent: Chrome");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "Content-Type: application/x-www-form-urlencoded");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "UA-CPU: *");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "Accept-Encoding: gzip, deflate");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "Connection: Keep-Alive");
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendString(sendbuf, nIndex, "Cache-Control: no-cache");
		nIndex = AppendSeparator(sendbuf, nIndex);

		wsprintfA(tbuf, "Content-Length: %d", String::Length(lpszPostData));
		//nIndex = AppendString(sendbuf, nIndex, "Content-Length: 24");
		nIndex = AppendString(sendbuf, nIndex, tbuf);
		nIndex = AppendSeparator(sendbuf, nIndex);
		nIndex = AppendSeparator(sendbuf, nIndex);
		//nIndex = AppendString(sendbuf, nIndex, "clientMD5=adfasdfadfasdf");
		nIndex = AppendString(sendbuf, nIndex, lpszPostData);

		//wsprintfA(tbuf, "Length(sendbuf): %d, nIndex: %d\r\nBuffer: %s", String::Length(sendbuf), nIndex, sendbuf);
		Debug::Assert(sendbuf);

		//---------------------------------------------------
		ir = send(s, sendbuf, nIndex, 0);
		if (ir == SOCKET_ERROR) 
		{
			//Debug::AssertEx("send() failed with error: %d\n", WSAGetLastError());
			closesocket(s);
			return FALSE;
		}


		return TRUE;
	}

	BOOL RevieceResponse(char* recvbuf, int len, SOCKET s)
	{
		int ir;

		// wierd?
		Sleep(250);

		ir = recv(s, recvbuf, len, 0);
		if(ir <= 0)
		{
			//Debug::AssertEx("recv failed with error: %d\n", WSAGetLastError());
			closesocket(s);
			return FALSE;
		}

		Debug::Assert(recvbuf);

		return TRUE;
	}

	BOOL DisconnectFromServer(SOCKET s)
	{
		int ir;

		//---------------------------------------------------
		ir = shutdown(s, SD_BOTH);
		if(ir == SOCKET_ERROR) 
		{
			//Debug::AssertEx("shutdown failed with error: %d\n", WSAGetLastError());
			closesocket(s);
			return FALSE;
		}

		//---------------------------------------------------
		ir = closesocket(s);
		if(ir == SOCKET_ERROR) 
		{
			//Debug::AssertEx("closesocket failed with error: %d\n", WSAGetLastError());
			return FALSE;
		}

		return TRUE;
	}

	BOOL ConvertToUTF8(char* recvbuf, int len, OUT BSTR* bstrStream)
	{
		WCHAR lpszXml[MAX_CHAR];
		GetXmlFromResponse(recvbuf, MAX_CHAR, lpszXml);

		if(String::Contains(lpszXml, L"<"))
			*bstrStream = String::Alloc(lpszXml);
		else
			return FALSE;

		return TRUE;
	}

	VOID FinalizeWSA()
	{
		WSACleanup();
	}



	//e.g.	call	  http://localhost:81/mms/centro/joke.asmx/TryValidateClient with clientMD5=51785694
	//
	//lpszRemoteURL	- http://localhost:81/mms/centro/joke.asmx
	//lpszMethod	- TryVerifyClient
	//lpszPostData	- clientMD5=51785694
	BOOL InvokeWebMethod(LPCSTR lpszRemoteURL, LPCSTR lpszMethod, LPCSTR lpszPostData, OUT BSTR* bstrStream)
	{
		BOOL fr = TRUE;
		SOCKET s = INVALID_SOCKET;
		char recvbuf[SOCKET_DEFAULT_BUFFER_LENGTH] = {0};

		fr &= InitializeWSA();

		if(fr)
		{
			fr &= ConnectToServer(lpszRemoteURL, &s);
			fr &= SendRequest(lpszRemoteURL, lpszMethod, lpszPostData, s);
			fr &= RevieceResponse(recvbuf, SOCKET_DEFAULT_BUFFER_LENGTH, s);
			fr &= DisconnectFromServer(s);
			fr &= ConvertToUTF8(recvbuf, SOCKET_DEFAULT_BUFFER_LENGTH, bstrStream);

			FinalizeWSA();
		}

		return fr;
	}



}}}}}